Skip to content

sea: mount bundled assets as a virtual file system - #65675

Merged
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
mcollina:vfs-sea-integration
Sep 3, 2026
Merged

sea: mount bundled assets as a virtual file system#65675
nodejs-github-bot merged 1 commit into
nodejs:mainfrom
mcollina:vfs-sea-integration

Conversation

@mcollina

@mcollina mcollina commented Aug 31, 2026

Copy link
Copy Markdown
Member

This adds the SEA integration for the virtual file system landed in #63653: setting "useVfs": true in the SEA configuration mounts the bundled assets as a read-only VFS and runs the injected main script from inside the mount.

Since a VFS never shadows the real file system and its mount point is reserved and chosen at runtime, there is no fixed /sea path. Instead, the main script is placed at the root of the mount and executed from there, so:

  • __filename and __dirname point inside the virtual file system;
  • assets are reached through __dirname-relative paths with regular node:fs APIs;
  • relative require() calls and node_modules lookups resolve against the bundled assets, confined to the mount by the module loader integration.

Implementation notes:

  • The main script is not duplicated into the assets at build time; its source already lives in the SEA blob and is injected into the provider at runtime.
  • SEAProvider is read-only and lazy: asset content stays in the executable's SEA blob and is copied into JS memory only when a file is opened, with sizes cached for stat.
  • The implicit SEA mount does not emit the VirtualFileSystem experimental warning; the SEA warning already covers it.
  • ESM entry points ("mainFormat": "module") are supported: the ESM main is loaded from inside the mount through the ESM loader, so import.meta.url/filename/dirname reflect the mount and static imports, dynamic import(), and bare specifier lookups resolve against the bundled assets.
  • "useVfs" is rejected together with "useSnapshot" and "useCodeCache".

This PR was prepared with the help of AI. I've reviewed all changes myself.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/single-executable

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. labels Aug 31, 2026
@mcollina
mcollina force-pushed the vfs-sea-integration branch 3 times, most recently from 148a0d7 to 4991e64 Compare August 31, 2026 09:14
@mcollina
mcollina requested review from Qard and joyeecheung and removed request for Qard August 31, 2026 10:46
pipobscure added a commit to pipobscure/bundles that referenced this pull request Sep 1, 2026
The tool was one directory of JavaScript that doubled as a library by
accident. This makes the library the point: `src/` is TypeScript compiled
to ESM in `dist/`, every capability has a declared entry point, and the
CLI is a thin wrapper over the same functions an embedder gets.

Four things are now separately importable, which is the shape the rest of
the design needs:

  .           create / sign / verify / inspect / run, from code (api.ts)
  ./record    the -r preload that writes down what a run reads
  ./register  the -r preload that mounts only what is signed
  ./sea       build and boot a self-validating executable

plus ./provider, ./recorder, ./cli, ./manifest, ./archive, ./files,
./skill, ./sigstore and ./oidc for the layers underneath. The package root
deliberately does not re-export the two providers: importing either needs
node:vfs, and creating or verifying an archive does not, so a plain
`import '@pipobscure/bundle'` must not drag `--experimental-vfs` in.
There is a test that holds that line.

The sources are erasable-syntax-only, so `node src/main.ts` runs them
directly under node's type stripping. That is what lets the tests import
the sources rather than the build, and a preload be `-r ./src/register.ts`.
@types/node carries neither the node:zlib ZIP API nor node:vfs's provider
registry, so `src/types/` declares them; the shapes were checked against
the running runtime rather than transcribed from documentation.

sea.js is replaced by src/sea.ts. The old bootstrap was a copy of
manifest.js inlined into a CommonJS file, which had drifted: its signature
marker regex was still the two-field form, so it read every
sigstore-signed container as unsigned. The replacement mounts this package
out of the SEA blob with node:vfs and requires the real library from
there — the userland form of nodejs/node#65675, with the difference that
the mount running the application is the signed archive appended to the
file. Nothing is duplicated, and the verifier a container runs is the one
the test suite tests. `bundle sea` builds one.

Three bugs the type checker and the new tests turned up, all real:

  * --identity and BUNDLE_IDENTITY were only consulted on the sigstore
    path, so an archive signed against an ordinary CA — which carries no
    identity claim at all — satisfied a policy demanding one. A machine
    configured to run only releases from a workflow would have mounted
    anything key-signed. It now reports valid-untrusted.

  * Arguments beginning with `--` never reached a mounted application;
    node claimed them as its own flags. mountArgv() now ends with `--`.

  * Verifying from a Buffer crashed, and sigstore signing was written
    against @sigstore/bundle v2 while v3 is installed. Both fixed, and the
    Buffer path now has a test.

131 tests over the format, both providers, the API, the CLI, the SEA and
the published package's own shape, replacing three JavaScript suites.
@mcollina
mcollina marked this pull request as ready for review September 1, 2026 15:04
@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.40476% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.06%. Comparing base (4d9cb71) to head (70c5a3f).
⚠️ Report is 65 commits behind head on main.

Files with missing lines Patch % Lines
src/node_sea.cc 93.47% 0 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65675      +/-   ##
==========================================
+ Coverage   90.05%   90.06%   +0.01%     
==========================================
  Files         754      756       +2     
  Lines      256308   257020     +712     
  Branches    48464    48579     +115     
==========================================
+ Hits       230817   231494     +677     
- Misses      16593    16634      +41     
+ Partials     8898     8892       -6     
Files with missing lines Coverage Δ
lib/internal/main/embedding.js 91.89% <100.00%> (+2.92%) ⬆️
lib/internal/vfs/providers/sea.js 100.00% <100.00%> (ø)
lib/internal/vfs/sea.js 100.00% <100.00%> (ø)
src/node_sea.h 100.00% <ø> (ø)
src/node_sea.cc 89.77% <93.47%> (+0.27%) ⬆️

... and 48 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread doc/api/single-executable-applications.md Outdated
Support "useVfs": true in the SEA configuration: mount the bundled
assets as a read-only VFS and run the CommonJS or ESM main script from
inside the mount.

Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina
mcollina force-pushed the vfs-sea-integration branch from de3de10 to 70c5a3f Compare September 2, 2026 06:52
@mcollina mcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 2, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 2, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@mcollina mcollina added the commit-queue PRs queued for automated landing through the Commit Queue. label Sep 3, 2026
@nodejs-github-bot
nodejs-github-bot merged commit 4e207b1 into nodejs:main Sep 3, 2026
72 checks passed
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Landed in 4e207b1

@nodejs-github-bot nodejs-github-bot removed the commit-queue PRs queued for automated landing through the Commit Queue. label Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants